using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;

namespace Utilities
{
	/// <summary>
	/// Summary description for ImagingTiffUtilities.
	/// </summary>
	public class TiffImages
	{
		[DllImport("kernel32.dll", EntryPoint="SetProcessWorkingSetSize", ExactSpelling=true, CharSet=CharSet.Ansi, SetLastError=true)] 
		private static extern int SetProcessWorkingSetSize(IntPtr process, int minimumWorkingSetSize, int maximumWorkingSetSize); 

		
		/// <summary>
		/// Moves the first page of the tiff to the end
		/// </summary>
		/// <param name="fileName"></param>
		/// <returns></returns>
		public static bool MoveFirstToLast(string fileName)
		{
			Encoder save = Encoder.SaveFlag;
			Encoder qual = Encoder.Quality;
			Encoder comp = Encoder.Compression;
			EncoderParameters ep = new EncoderParameters(2);
			
			ep.Param[0] = new EncoderParameter(save,Convert.ToInt32(EncoderValue.MultiFrame));			
			ep.Param[1] = new EncoderParameter(comp,Convert.ToInt32(EncoderValue.CompressionCCITT4));
			//ep.Param[2] = new EncoderParameter(qual,100L);

			Image currentImage = Image.FromFile(fileName);
			System.Drawing.Bitmap newImage = null;
			System.Drawing.Bitmap newImageTemp = null;
			System.Drawing.Imaging.ImageCodecInfo ic = null;
		
			foreach(System.Drawing.Imaging.ImageCodecInfo ii in System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders())
			{
				if(ii.MimeType == "image/tiff")
				{
					ic = ii;
				}	
			}
	
			FrameDimension fdim = new System.Drawing.Imaging.FrameDimension(currentImage.FrameDimensionsList[0]); 						
			int numberOfPages = currentImage.GetFrameCount(fdim);
					
			bool firstPage = true;
			//Skip first page, we'll put it last
			for(int i = 1;i < numberOfPages;i++)
			{
				currentImage.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page,i);
					
				if(firstPage)
				{
					firstPage = false;
								
					newImage = (Bitmap)currentImage;
					newImage.SetResolution(300,300);
								
					newImage.Save(@"c:\temp\images\mikenew.tif",ic,ep);					
				}
				else
				{								
					ep.Param[0] = new EncoderParameter(save,Convert.ToInt32(EncoderValue.FrameDimensionPage));					
					ep.Param[1] = new EncoderParameter(comp,Convert.ToInt32(EncoderValue.CompressionCCITT4));
					//ep.Param[2] = new EncoderParameter(qual,100L);
					newImageTemp = (Bitmap)currentImage;
					newImageTemp.SetResolution(300,300);
					newImage.SaveAdd(newImageTemp,ep);				
				}
			}

			currentImage.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page,0);

			ep.Param[0] = new EncoderParameter(save,Convert.ToInt32(EncoderValue.FrameDimensionPage));					
			ep.Param[1] = new EncoderParameter(comp,Convert.ToInt32(EncoderValue.CompressionCCITT4));
			//ep.Param[2] = new EncoderParameter(qual,100L);
			newImageTemp = (Bitmap)currentImage;
			newImageTemp.SetResolution(300,300);
			newImage.SaveAdd(newImageTemp,ep);
			
			currentImage.Dispose();
			newImage.Dispose();
			newImageTemp.Dispose();

			return true;
		}

		//		private Bitmap InvertColor(Image image)
		//		{
		//			if (Image.PixelFormat == Atalasoft.Imaging.PixelFormat.Pixel1bppIndexed)
		//			{
		//				// See if the 0 palette entry is less than the 1 palette entry.
		//				Color p0 = Image.Palette.GetEntry(0);
		//				Color p1 = Image.Palette.GetEntry(1);
		//				if (p0.ToArgb() > p1.ToArgb())
		//				{
		//					// Swap the palette entries.
		//					Image.Palette.SetEntry(0, p1);
		//					Image.Palette.SetEntry(1, p0);
		//
		//					// Invert the image data.
		//					Atalasoft.Imaging.ImageProcessing.Channels.InvertCommand inv = new Atalasoft.Imaging.ImageProcessing.Channels.InvertCommand();
		//					inv.ApplyToImage(Image);
		//				}
		//			}
		//
		//			return Image;
		//
		//
		//		}

		/// <summary>
		/// Break out a new tiff from an existing one
		/// </summary>
		/// <param name="currentTif"></param>
		/// <param name="startingIndex"></param>
		/// <param name="totalPages"></param>
		/// <param name="newTifName"></param>
		public static void BreakoutNewTiff(Bitmap currentTif,int startingIndex,int totalPages,string newTifName)
		{
			FrameDimension fdim = new System.Drawing.Imaging.FrameDimension((currentTif as Image).FrameDimensionsList[0]); 						
			int numberOfPages = currentTif.GetFrameCount(fdim);

			if(numberOfPages >= (startingIndex + totalPages - 1))
			{
				Encoder save = Encoder.SaveFlag;
				Encoder qual = Encoder.Quality;
				Encoder comp = Encoder.Compression;
				EncoderParameters ep = new EncoderParameters(2);
				ep.Param[0] = new EncoderParameter(save,Convert.ToInt32(EncoderValue.MultiFrame));			
				ep.Param[1] = new EncoderParameter(comp,Convert.ToInt32(EncoderValue.CompressionCCITT4));
				//ep.Param[2] = new EncoderParameter(qual,100L);
			
				System.Drawing.Bitmap newImage = null;
				System.Drawing.Bitmap newImageTemp = null;
				System.Drawing.Imaging.ImageCodecInfo ic = null;
		
				foreach(System.Drawing.Imaging.ImageCodecInfo ii in System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders())
				{
					if(ii.MimeType == "image/tiff")
					{
						ic = ii;
					}	
				}				
						
				bool firstPage = true;

				for(int i = startingIndex; i <= startingIndex + totalPages - 1;i++)
				{
					currentTif.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page,i);
				

					if(firstPage)
					{
						firstPage = false;
								
						newImage = (Bitmap)currentTif;
						newImage.SetResolution(300,300);
								
						newImage.Save(newTifName,ic,ep);					
					}
					else
					{								
						ep.Param[0] = new EncoderParameter(save,Convert.ToInt32(EncoderValue.FrameDimensionPage));					
						ep.Param[1] = new EncoderParameter(comp,Convert.ToInt32(EncoderValue.CompressionCCITT4));
						//ep.Param[2] = new EncoderParameter(qual,100L);
						newImageTemp = (Bitmap)currentTif;
						newImageTemp.SetResolution(300,300);
						newImage.SaveAdd(newImageTemp,ep);				
					}
				}
			
				currentTif.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page,0);
						
				if(newImage != null)
				{
					newImage.Dispose();
				}
				if(newImageTemp != null)
				{
					newImageTemp.Dispose();
				}
			}

		}

		/// <summary>
		/// Merges all Tiffs in a folder into one Tiff specified by outPutFile. The tiffs to merge need
		/// to be named sequentially so that they will in the correct order in the final tiff
		/// </summary>
		/// <param name="dir"></param>
		/// <param name="outPutFile"></param>
		public static void MergeTifs(System.IO.DirectoryInfo dir,string outPutFile)
		{
			Encoder save = Encoder.SaveFlag;
			Encoder qual = Encoder.Quality;
			Encoder comp = Encoder.Compression;
			EncoderParameters ep = new EncoderParameters(2);
			
			ep.Param[0] = new EncoderParameter(save,Convert.ToInt32(EncoderValue.MultiFrame));			
			ep.Param[1] = new EncoderParameter(comp,Convert.ToInt32(EncoderValue.CompressionCCITT4));
			//ep.Param[2] = new EncoderParameter(qual,100L);
			
			System.Drawing.Bitmap newImage = null;
			System.Drawing.Bitmap newImageTemp = null;
			System.Drawing.Imaging.ImageCodecInfo ic = null;
		
			foreach(System.Drawing.Imaging.ImageCodecInfo ii in System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders())
			{
				if(ii.MimeType == "image/tiff")
				{
					ic = ii;
				}	
			}
						
			bool firstPage = true;
			//Skip first page, we'll put it last
			
			foreach(System.IO.FileSystemInfo finfo in dir.GetFileSystemInfos("*.tif"))
			{
					
				if(firstPage)
				{
					firstPage = false;
								
					newImage = (Bitmap)Bitmap.FromFile(finfo.FullName);
					newImage.SetResolution(300,300);
								
					newImage.Save(outPutFile,ic,ep);					
				}
				else
				{								
					ep.Param[0] = new EncoderParameter(save,Convert.ToInt32(EncoderValue.FrameDimensionPage));					
					ep.Param[1] = new EncoderParameter(comp,Convert.ToInt32(EncoderValue.CompressionCCITT4));
					//ep.Param[2] = new EncoderParameter(qual,100L);
					newImageTemp = (Bitmap)Bitmap.FromFile(finfo.FullName);
					newImageTemp.SetResolution(300,300);
					newImage.SaveAdd(newImageTemp,ep);				
				}
			}
			
						
			newImage.Dispose();
			newImageTemp.Dispose();
		}

		/// <summary>
		/// Merges specific Tiffs into one Tiff specified by outPutFile. The tiffs to merge need
		/// to be named sequentially so that they will in the correct order in the final tiff
		/// </summary>
		/// <param name="dir">The dir where the tifs are located</param>
		/// <param name="tifs">The tifs in an ArrayList</param>
		/// <param name="outPutFile">The output file.</param>
		public static void MergeSpecificTifs(string dir, ArrayList tifs, string outPutFile)
		{
			Encoder save = Encoder.SaveFlag;
			Encoder qual = Encoder.Quality;
			Encoder comp = Encoder.Compression;
			EncoderParameters ep = new EncoderParameters(2);
			
			ep.Param[0] = new EncoderParameter(save,Convert.ToInt32(EncoderValue.MultiFrame));			
			ep.Param[1] = new EncoderParameter(comp,Convert.ToInt32(EncoderValue.CompressionCCITT4));
			//ep.Param[2] = new EncoderParameter(qual,100L);
			
			System.Drawing.Bitmap newImage = null;
			System.Drawing.Bitmap newImageTemp = null;
			System.Drawing.Bitmap testImageTemp = null;
			System.Drawing.Imaging.ImageCodecInfo ic = null;
		
			foreach(System.Drawing.Imaging.ImageCodecInfo ii in System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders())
			{
				if(ii.MimeType == "image/tiff")
				{
					ic = ii;
				}	
			}
						
			bool firstPage = true;
			bool additionalPages = false;
			bool saveImage = true;
			//Skip first page, we'll put it last
			
			//foreach(System.IO.FileSystemInfo finfo in dir.GetFileSystemInfos("*.tif"))
			for(int i=0;i<tifs.Count;i++)
			{
				if(firstPage)
				{
					firstPage = false;
					
					if(dir.EndsWith("\\"))
					{
						try
						{
							newImage = (Bitmap)Bitmap.FromFile(dir+tifs[i]);
						}
						catch (Exception ex)
						{
							//Handle Exception
							firstPage = true;
							saveImage = false;
						}
						
					}
					else
					{
						try
						{
							newImage = (Bitmap)Bitmap.FromFile(dir+"\\"+tifs[i]);
						}
						catch (Exception ex)
						{
							//Handle Exception
							firstPage = true;
							saveImage = false;

						}
						
					}
					
					if(saveImage)
					{
						newImage.SetResolution(300,300);
								
						newImage.Save(outPutFile,ic,ep);
					}
					
				}
				else
				{								
					ep.Param[0] = new EncoderParameter(save,Convert.ToInt32(EncoderValue.FrameDimensionPage));					
					ep.Param[1] = new EncoderParameter(comp,Convert.ToInt32(EncoderValue.CompressionCCITT4));
					//ep.Param[2] = new EncoderParameter(qual,100L);

					if(dir.EndsWith("\\"))
					{
						newImageTemp = (Bitmap)Bitmap.FromFile(dir+tifs[i]);
					}
					else
					{
						newImageTemp = (Bitmap)Bitmap.FromFile(dir+"\\"+tifs[i]);
					}
					newImageTemp.SetResolution(300,300);
					newImage.SaveAdd(newImageTemp,ep);
					additionalPages = true;
				}
				saveImage = true;
			}
			
						
			newImage.Dispose();
			if(additionalPages)
			{
				newImageTemp.Dispose();
			}
			FlushMemory();
		}

		/// <summary>
		/// Gets the number of pages in a tif image.
		/// </summary>
		/// <param name="fileName">Name of the tif file.</param>
		/// <returns></returns>
		public static int GetNumberOfPagesTif(string fileName)
		{
			try
			{
				int Count = 0;

				Image currentImage = Image.FromFile(fileName);
				FrameDimension fdim = new System.Drawing.Imaging.FrameDimension(currentImage.FrameDimensionsList[0]); 						
				Count = currentImage.GetFrameCount(fdim);

				return Count;
			}
			catch(Exception ex)
			{
				return GetNumberOfPagesTif2(fileName);
			}
		}

		public static int GetNumberOfPagesTif2(string fileName)
		{
			Bitmap bm = (Bitmap) Bitmap.FromFile(fileName);

			return bm.GetFrameCount(FrameDimension.Page);
		}


		/// <summary>
		/// Merges all frames from a Source Tiff or Directory into an existing Tiff specified by masterFile.
		///
		/// </summary>
		/// <param name="dir"></param>
		/// <param name="outPutFile"></param>
		public static void MergeTifsExisting(System.IO.DirectoryInfo dir, string sourceFile, string masterFile)
		{
			try
			{
				Encoder saveEncoder;
				Encoder compressionEncoder;
				EncoderParameter SaveEncodeParam;
				EncoderParameter CompressionEncodeParam;
				EncoderParameters EncoderParams = new EncoderParameters(2);
				Bitmap pages;
				Bitmap NextPage;
				Bitmap ImageMaster = null;
				Bitmap ImageSource = null;
				System.Drawing.Imaging.ImageCodecInfo ic = null;

				// load codecs?
				foreach (System.Drawing.Imaging.ImageCodecInfo ii in System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders())
				{
					if (ii.MimeType == "image/tiff")
					{
						ic = ii;
					}
				}


				ImageMaster = (Bitmap)Bitmap.FromFile(masterFile);

				saveEncoder = Encoder.SaveFlag;
				compressionEncoder = Encoder.Compression;

				ImageMaster.SelectActiveFrame(FrameDimension.Page, 0);
				pages = new Bitmap(ImageMaster);
				//pages = InvertColor(Atalasoft.Imaging.AtalaImage.FromBitmap(pages)).GetBitmap();
				pages = ConvertToBitonal(pages);

				// Save the first page (frame) of master image to new file
				SaveEncodeParam = new EncoderParameter(saveEncoder, (long)EncoderValue.MultiFrame);
				CompressionEncodeParam = new EncoderParameter(compressionEncoder, (long)EncoderValue.CompressionCCITT4);
				EncoderParams.Param[0] = CompressionEncodeParam;
				EncoderParams.Param[1] = SaveEncodeParam;

				pages.Save(masterFile + ".merge", ic, EncoderParams); // use a temporary file to store the results and rename at the end

				// get number of pages in master image
				FrameDimension fdim = new System.Drawing.Imaging.FrameDimension((ImageMaster as Image).FrameDimensionsList[0]);
				int PageNumber = ImageMaster.GetFrameCount(fdim);

				// save remainder of master image to temp file
				for (int i = 1; i < PageNumber; i++)
				{
					SaveEncodeParam = new EncoderParameter(saveEncoder, (long)EncoderValue.FrameDimensionPage);
					CompressionEncodeParam = new EncoderParameter(compressionEncoder, (long)EncoderValue.CompressionCCITT4);
					EncoderParams.Param[0] = CompressionEncodeParam;
					EncoderParams.Param[1] = SaveEncodeParam;

					ImageMaster.SelectActiveFrame(FrameDimension.Page, i);
					if(ImageMaster.Height >0 && ImageMaster.Width>0)
					{
						NextPage = new Bitmap(ImageMaster);
						NextPage = ConvertToBitonal(NextPage);
						pages.SaveAdd(NextPage, EncoderParams);
					}

				}

				// check if merging w/ Dirctory or one Source File
				if (dir == null)
				{
					ImageSource = (Bitmap)Bitmap.FromFile(sourceFile);

					// get number of pages in source image
					fdim = new System.Drawing.Imaging.FrameDimension((ImageSource as Image).FrameDimensionsList[0]);
					PageNumber = ImageSource.GetFrameCount(fdim);

					// save source image to temp file
					for (int i = 0; i < PageNumber; i++)
					{
						SaveEncodeParam = new EncoderParameter(saveEncoder, (long)EncoderValue.FrameDimensionPage);
						CompressionEncodeParam = new EncoderParameter(compressionEncoder, (long)EncoderValue.CompressionCCITT4);
						EncoderParams.Param[0] = CompressionEncodeParam;
						EncoderParams.Param[1] = SaveEncodeParam;

						ImageSource.SelectActiveFrame(FrameDimension.Page, i);
						if(ImageSource.Height >0 && ImageSource.Width>0)
						{
							NextPage = new Bitmap(ImageSource);
							NextPage = ConvertToBitonal(NextPage);
							pages.SaveAdd(NextPage, EncoderParams);
						}

					}
				}
				else
				{
					foreach (System.IO.FileSystemInfo fsysinfo in dir.GetFileSystemInfos("*.tif"))
					{
						if (fsysinfo.FullName == masterFile) 
							continue;
						else
						{
							ImageSource = (Bitmap)Bitmap.FromFile(fsysinfo.FullName);

							// get number of pages in source image
							fdim = new System.Drawing.Imaging.FrameDimension((ImageSource as Image).FrameDimensionsList[0]);
							PageNumber = ImageSource.GetFrameCount(fdim);

							// save source image to temp file
							for (int i = 0; i < PageNumber; i++)
							{
								SaveEncodeParam = new EncoderParameter(saveEncoder, (long)EncoderValue.FrameDimensionPage);
								CompressionEncodeParam = new EncoderParameter(compressionEncoder, (long)EncoderValue.CompressionCCITT4);
								EncoderParams.Param[0] = CompressionEncodeParam;
								EncoderParams.Param[1] = SaveEncodeParam;

								ImageSource.SelectActiveFrame(FrameDimension.Page, i);
								if(ImageSource.Height >0 && ImageSource.Width>0)
								{
									NextPage = new Bitmap(ImageSource);
									NextPage = ConvertToBitonal(NextPage);
									pages.SaveAdd(NextPage, EncoderParams);
								}

							}
						}
					}
				}

				// cleanup
				SaveEncodeParam = new EncoderParameter(saveEncoder, (long)EncoderValue.Flush);
				EncoderParams.Param[0] = SaveEncodeParam;
				pages.SaveAdd(EncoderParams);

				pages.Dispose();
				ImageMaster.Dispose();
				ImageSource.Dispose();

				// rename original file
				System.IO.FileInfo finfo = new System.IO.FileInfo(masterFile);
				finfo.CopyTo(masterFile + ".bak", true);

				// overwrite original with temp file
				finfo = new System.IO.FileInfo(masterFile + ".merge");
				finfo.CopyTo(masterFile, true);
              
				// delete temp file
				finfo.Delete();
			}
			catch (System.Exception ee)
			{
				throw ee;
			}
			FlushMemory();
		}

		/// <summary>
		/// Walks each pixel to change pixel format necessary for the groupIV compression
		/// </summary>
		/// <param name="original"></param>
		public static Bitmap ConvertToBitonal(Bitmap original)
		{
			FlushMemory();

			Bitmap source = null;

			// If original bitmap is not already in 32 BPP, ARGB format, then convert
			if (original.PixelFormat != PixelFormat.Format32bppArgb)
			{
				source = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
				source.SetResolution(original.HorizontalResolution, original.VerticalResolution);
				using (Graphics g = Graphics.FromImage(source))
				{
					g.DrawImageUnscaled(original, 0, 0);
				}
			}
			else
			{
				source = original;
			}

			// Lock source bitmap in memory
			BitmapData sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

			// Copy image data to binary array
			Int32 imageSize = sourceData.Stride * sourceData.Height;
			byte[] sourceBuffer = new byte[imageSize];
			System.Runtime.InteropServices.Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, imageSize);

			// Unlock source bitmap
			source.UnlockBits(sourceData);

			// Create destination bitmap
			Bitmap destination = new Bitmap(source.Width, source.Height, PixelFormat.Format1bppIndexed);

			// Lock destination bitmap in memory
			BitmapData destinationData = destination.LockBits(new Rectangle(0, 0, destination.Width, destination.Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);

			// Create destination buffer
			imageSize = destinationData.Stride * destinationData.Height;
			byte[] destinationBuffer = new byte[imageSize];

			int sourceIndex = 0;
			int destinationIndex = 0;
			int pixelTotal = 0;
			byte destinationValue = 0;
			int pixelValue = 128;
			int height = source.Height;
			int width = source.Width;
			int threshold = 500;

			// Iterate lines
			for (int y = 0; y < height; y++)
			{
				sourceIndex = y * sourceData.Stride;
				destinationIndex = y * destinationData.Stride;
				destinationValue = 0;
				pixelValue = 128;

				// Iterate pixels
				for (int x = 0; x < width; x++)
				{
					// Compute pixel brightness (i.e. total of Red, Green, and Blue values)
					pixelTotal = sourceBuffer[sourceIndex + 1] + sourceBuffer[sourceIndex + 2] + sourceBuffer[sourceIndex + 3];
					if (pixelTotal > threshold)
					{
						destinationValue += (byte)pixelValue;
					}
					if (pixelValue == 1)
					{
						destinationBuffer[destinationIndex] = destinationValue;
						destinationIndex++;
						destinationValue = 0;
						pixelValue = 128;
					}
					else
					{
						pixelValue >>= 1;
					}
					sourceIndex += 4;
				}
				if (pixelValue != 128)
				{
					destinationBuffer[destinationIndex] = destinationValue;
				}
			}

			// Copy binary image data to destination bitmap
			System.Runtime.InteropServices.Marshal.Copy(destinationBuffer, 0, destinationData.Scan0, imageSize);

			// Unlock destination bitmap
			destination.UnlockBits(destinationData);

			sourceBuffer = null;
			destinationBuffer = null;
			GC.Collect();
			// Return
			return destination;
		}

		public static void FlushMemory() 
		{ 
			GC.Collect(); 
			GC.WaitForPendingFinalizers(); 
			if (Environment.OSVersion.Platform == PlatformID.Win32NT) 
			{ 
				SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1); 
			} 
		}		
	}
}
